# 
# application_helper_methods.rb
# 
# Created on 22/08/2007, 14:36:25

module AutoRest
  module ApplicationHelperMethods
    
    # Will look for the parameter "meaning" given its type.
    # other_parameters will be used to pass additional data if needed 
    # (ie the form_builder in a new or edit view)
    def ar_retrieve_field_value parameter, resource, other_parameters= {}
      # If user passed title as string or symbol, simply display it.
      return parameter if String === parameter || Symbol === parameter
      
      params= {:parent_resource => @parent_resource, :resource => resource }
      params.merge!(other_parameters)
      
      case parameter
      # If user passed a ARHelperCall, call the method.
      when Struct::ARHelperCall: send(parameter.method_name, params)
        
      # If user passed a proc as title, call it 
      when Proc: parameter.call(params.merge({ :helper => self }))
      end
    end
    
    # Labels for columns in index.html table
    def ar_get_index_label title
      ar_retrieve_field_value title, @resource
    end
    
    # Send a message to the resource OR calls a block/method
    # This depends on the columns specified for each view in the controller class.
    def ar_get_resource_value(resource, column)
      return "Error: #{resource}, #{column}" unless column && resource
      
      case column
      when String, Symbol
        
        if controller.ardata.foreing_models.include?(column.to_s)
          column.to_s.match(/^(.*)_id$/)
          record= resource.send($1)
          return record ? record.label : controller.ardata.labels[:nil_value_in_field]
        else
          return h(resource.send(column)) || controller.ardata.labels[:nil_value_in_field]
        end
        
      else
        ar_retrieve_field_value column, resource
      end
    end

    #---------------------------------------------------------------------------    
    
    # Colspan for actions in index.rhtml table
    def ar_index_colspan
      colspan= 0
      colspan+= 1 if controller.ardata.links.include? :edit
      colspan+= 1 if controller.ardata.links.include? :show
      colspan+= 1 if controller.ardata.links.include? :destroy
      
      if colspan > 0
        content_tag(:th, '', :colspan => colspan) 
      else
        ""
      end
    end

    # th tags for the index table.
    def ar_index_table_headers    
      label_header= content_tag(:th, controller.ardata.labels[:label])
      
      # This will be used when adding the table rows.
      # it will contain, in the order in wich they are retrieved,
      # a true of false value depending on the presence of the label
      # for the table header. This means that a column can be disabled
      # simply by labeling it with an empty string (most usefull when the
      # label of the column comes from a method or a Proc that can enable/disable
      # the output given a set of conditions
      @display_current_ar_index_table_column= []
      
      headers= []
      
      controller.ardata.fields.for_index.each do |column, title|
        label= ar_get_index_label(title)
        
        if label && !label.strip.empty?
          headers << content_tag(:th, label) 
          @display_current_ar_index_table_column << true
        else
          @display_current_ar_index_table_column << false
        end
      end
      
      colspan= ar_index_colspan
      
      "<tr>\n        #{label_header}" \
          "\n        #{headers.join("\n        ")}" \
          "\n        #{colspan}" \
          "\n      </tr>"
    end
    
    # Build rows of actual data for index view's table.
    def ar_index_table_data
      returning("") do |result|
        
        for resource in @resources
          columns= [ar_label(resource)]

          controller.ardata.fields.for_index.each_with_index do |column_title, index|
            next if not @display_current_ar_index_table_column[index]
            columns << ar_get_resource_value(resource, column_title[0])
          end

          columns << ar_show_link(resource) if controller.ardata.links.include? :show
          columns << ar_edit_link(resource) if controller.ardata.links.include? :edit
          columns << ar_destroy_link(resource) if controller.ardata.links.include? :destroy

          columns= columns.map{|elem| content_tag :td, elem}.join("\n        ")
          columns= "\n        #{columns}\n      "

          result << "\n      #{content_tag(:tr, columns, :class => cycle("even", "odd")) }\n" 
        end

        #Die!! Array :) See ar_index_table_headers
        @display_current_ar_index_table_column= nil
      end
    end
    
    # Standard selection of field type given the column type.
    def ar_form_method form, resource, attribute, controller_ardata
      # Check if attribute is indeed a model relation foreign key
      case attribute
      when String, Symbol
        attr= attribute.to_s
        if controller_ardata.foreing_models.include? attr
          model= controller_ardata.foreing_models[attr]
          # because the attribute is a foreing key, build a select tag + options 
          # by looking in the related model
          ar_model_foreign_key_select resource, model, attr
        else
          # Attribute is not a fk, use a simple input field.
          ar_standard_form_method form, resource, attribute
        end
      else
        ar_retrieve_field_value attribute, resource, {:form => form}
      end
    end
    
    # Creation of a select tag given a foreign key
    # For customization of the selectable values, this method
    # will first search for a defined collection of records followin AR conventions.
    # ie.: for selecting records for a ProdFormat Class, it will
    # first search for a @prod_formats instance variable.
    # If it can't be located, it will ask the class of the resource being
    # selected. 
    # If present, a self.ar_options method will be called in the AR class
    # for getting specific parameters for the search of selectable objects.
    def ar_model_foreign_key_select resource, model, attribute
      
      collection= instance_variable_get("@#{model.class_name.underscore.pluralize}")
      
      # if the instance variable don't exist, find the options using the model.
      if !collection
        options= model.respond_to?(:ar_options) ? model.ar_options(resource) : nil
        collection= options ? model.find(:all, options) : model.find(:all)
      end
      
      selected= resource.respond_to?(attribute) ? resource.send(attribute) : nil
      
      # This fix a bug in wich the selected value is compared with different types,
      # for example whe I pass a selected id of a product, the rails helper compares
      # selected_value == value_of_option, but always gives false because
      # 77 == "77" is false! For that reason I convert the value to a fixnum
      selected= selected.to_i if String === selected && selected =~ /^\d+$/
      
      option_tags= "\n<option></option>\n" \
        "#{options_from_collection_for_select(collection, 'id', 'label', selected)}\n"
      
      tag= select_tag "resource[#{attribute}]", option_tags, :id => "resource_#{attribute}"
      
      # check for errors and signal them whith the proper css class.
      if resource.errors.on attribute
        content_tag :div, tag, :class => "fieldWithErrors"
      else
        tag
      end
    end
    
    # This Helper returns the apropiate form method
    # for the resource and attribute
    # TODO: This is pretty ugly :) ... 
    # Is there any other way to acomplish this functionality?
    def ar_standard_form_method form, resource, attribute
      # I use instance_variable_get to avoid deprecation of "type" warning
      case resource.column_for_attribute(attribute).instance_variable_get("@type")
      when :text
        form.text_area(attribute)  
      when :binary, :boolean
        form.check_box(attribute)
      else
      #when :string, :integer, :float, :decimal
      #when :datetime, :timestamp, :time, :date
        form.text_field(attribute)
      end
    end
    
    # data to be showed in "show" view
    def ar_show_data 
      result= []
      
      controller.ardata.fields.for_show.each do |column, title|
        label= ar_get_index_label(title)
        if label && !label.empty?
          result << content_tag(:dt, label)
          result << content_tag(:dd, ar_get_resource_value(@resource, column))
        end
      end
      
      content_tag :dl, result.join("\n")
    end
   
    # Generates lock version error messages if found on record.
    def ar_lock_version_errors
      return if !flash[:lock_version_error]
      
      errors= []
      # Display the user submited values that caused the lock version errors.
      controller.ardata.fields.for_edit.each do |column, title|
        errors << content_tag(:li, "#{content_tag(:strong, title)}: #{params[:resource][column]}")
      end
      errors= content_tag(:ul, errors.join("\n"))
        
      content_tag :div, "#{flash[:lock_version_error]}\n#{errors}", :class=> :important
    end
    
    # Form to input data on the new or edit view
    def ar_form_for new_or_edit, builder
      result= "\n"
      
      if new_or_edit != :new && @resource.has_attribute?(:lock_version)
        result << content_tag(:dd, builder.hidden_field(:lock_version)) << "\n"
      end

      controller.ardata.hidden_fields.each do |hidden_attr|
        result << content_tag(:dd, builder.hidden_field(hidden_attr)) << "\n"
      end

      collection= controller.ardata.fields.send("for_#{new_or_edit}")
      
      collection.each do |column, title|
        # This skips the selection of a parent on create if the parent is present (because if the resource
        # is child of a known parent the value for the parent_id is already known.
        next if new_or_edit == :new && controller.ardata.is_column_for_current_parent(column, @parent_resource)

        label= ar_get_index_label(title)

        # Only display the field if it's not empty. This is usefull when the label
        # is built from a proc: you can decide wheter to show the field or not by 
        # returning and empty label for the attribute based on certain condition.
        if label && !label.strip.empty?
          field= ar_form_method(builder, @resource, column, controller.ardata)
          result << "#{content_tag(:dt, label)}\n  #{content_tag(:dd,  field)}\n"
        end
      end

      label_key_for_submit= new_or_edit == :new ? :create : :update
      result << content_tag(:dt, submit_tag(controller.ardata.labels[label_key_for_submit])) << "\n"
      
      content_tag :dl, result
    end
    
    # --------------------------------------------------------------------------
    # CRUD Links / URLS
    # --------------------------------------------------------------------------
    
    # returns the path to the resource taking in account the presence of a parent resource.
    def ar_resource_path action, resource= @resource
      # resource_method is a helper of auto_rest that determines the url helper method
      # name based on the presence of the parent.
      meth= "#{controller.ardata.resource_method(@parent_resource, action)}_path"
      
      case 
      when @parent_resource && resource
        send meth, @parent_resource, resource
      when resource
        send meth, resource
      when @parent_resource
        send meth, @parent_resource
      else
        send meth
      end
    end
    
    # returns the path to the resources list taking in account the presence of a parent resource.
    def ar_resources_path
      meth= "#{controller.ardata.resources_method(@parent_resource, '')}_path"
      @parent_resource ? send(meth, @parent_resource) : send(meth)
    end
    
    # --------------------------------------------------------------------------
    
    # label of the resource and link to its controller.
    def ar_label resource= @resource
      return controller.ardata.labels[:nil_value_in_field] unless resource
      if controller.ardata.links.include? :show
        link_to(resource.label || "-", ar_resource_path('', resource))
      else
        resource.label || "-"
      end
    end
    
    # Almost the same as ar_label, but with an action label instead of a resource label.
    def ar_show_link resource= @resource
      link_to(controller.ardata.labels[:show], ar_resource_path('', resource))
    end
    
    def ar_edit_link  resource= @resource
      link_to(controller.ardata.labels[:edit], ar_resource_path('edit_', resource))
    end
    
    def ar_destroy_link  resource= @resource
      link_to(controller.ardata.labels[:destroy], ar_resource_path('', resource),
              :confirm => controller.ardata.labels[:destroy_confirm], 
              :method => :delete)
    end
    
    def ar_create_link
      link_to controller.ardata.labels[:new], ar_resource_path('new_', nil)
    end
    
    def ar_back_link
      link_to controller.ardata.labels[:back], ar_resources_path
    end
    
    def ar_search_link
      link_to controller.ardata.labels[:search], send("search_#{controller.ardata.resource.plural}_path")
    end
    
    # --------------------------------------------------------------------------
    # Search results helpers
    # --------------------------------------------------------------------------    

    # Render the ferret results on table customers
    # tags: :enclose => :p, :items => :ul, :item => :li
    def ar_search_results tags= {}
      if !@query || @query.empty? 
        result= controller.ardata.labels[:ask_for_search_terms]
      elsif @search_results && @search_results.total_hits > 0

        result= sprintf(controller.ardata.labels[:search_results_summary], 
                        @search_results.total_hits, @query)
          
        items= @search_results.inject([]) do |elems_array, elem|
          elems_array << content_tag(tags[:item] || :li, ar_transform_search_result(elem))
        end

        result= "#{result}\n#{content_tag(tags[:items] || :ul, items.join("\n"))}"
      else
        result= sprintf(controller.ardata.labels[:search_results_summary], 0, @query)
      end
      
      content_tag(tags[:enclose] || :p, result)
    end
    
    # Build a search form
    def ar_search_form destination, tags= {}
      input_tag= content_tag(tags[:field] || :dt, text_field_tag(:query_terms, ''))
      submit_tag= content_tag(tags[:submit] || :dd, submit_tag('Buscar'))
      
      content= content_tag(tags[:enclose] || :dl, "#{input_tag} #{submit_tag}")
      
      open_form= form_tag(send("search_#{destination}_path"), :method => :get)
                
      "#{open_form}\n  #{content}\n</form>" 
    end
    
    # JavaScript Helper to "Ajaxify" the search page.
    def ar_observe_field_query_terms destination
      observe_field 'query_terms', :frequency => 2,
                                   :update => "auto_rest_index_table",
                                   :before => "Element.show('spinner')",
                                   :success => "Element.hide('spinner')",
                                   :url => send("search_#{destination}_path"), 
                                   :method => :get,
                                   :with => "query_terms"
    end

    # Transform each element of the result set to a nice form
    def ar_transform_search_result elem
      ar_label elem
    rescue
      elem.label
    end
    
  end
end

# Add view-helpers methods,
ActionView::Base.send :include, AutoRest::ApplicationHelperMethods